import http from '@ohos.net.http';
import { LoggingInterceptor } from './LoggingInterceptor';
import { HttpError, HttpInterceptor, RequestChain, ResponseChain } from './RequestOptions';
import { ServiceError } from './ServiceError';

export interface DownloadProgress {
  total: number;
  current: number;
  percentage: number;
}

export interface DownloadOptions {
  chunkSize?: number;      // 分块大小，默认 2MB
  maxRetries?: number;     // 最大重试次数，默认 3
  timeout?: number;        // 超时时间，默认 30s
  onProgress?: (progress: DownloadProgress) => void;  // 进度回调
}

export class HttpDownloader {
  private static instance: HttpDownloader;
  private extraHeaders: Map<string, string> = new Map();
  private interceptors: HttpInterceptor[] = [];
  private timeout: number = 30000;
  private maxRetries: number = 3;
  private agent: string = '';
  private isDownloadCancelled: boolean = false;
  private progressCallback?: (progress: DownloadProgress) => void;
  private defaultChunkSize: number = 2 * 1024 * 1024; // 2MB

  private constructor() {
    this.addInterceptor(new LoggingInterceptor());
  }

  public static getInstance(): HttpDownloader {
    if (!HttpDownloader.instance) {
      HttpDownloader.instance = new HttpDownloader();
    }
    return HttpDownloader.instance;
  }

  setProgressCallback(callback: (progress: DownloadProgress) => void): HttpDownloader {
    this.progressCallback = callback;
    return this;
  }

  cancelDownload(): void {
    this.isDownloadCancelled = true;
  }

  resetCancelStatus(): void {
    this.isDownloadCancelled = false;
  }

  private async request(method: http.RequestMethod, url: string, data?: any,
    type: http.HttpDataType = http.HttpDataType.ARRAY_BUFFER,
    options?: DownloadOptions): Promise<http.HttpResponse> {

    let retryCount = 0;
    let lastError: Error;
    this.resetCancelStatus();

    while (retryCount <= this.maxRetries && !this.isDownloadCancelled) {
      let httpRequest: http.HttpRequest | null = null;
      try {

        httpRequest = http.createHttp();

        // 基本请求配置
        const requestConfig: http.HttpRequestOptions = {
          method: method,
          readTimeout: options?.timeout || this.timeout,
          connectTimeout: options?.timeout || this.timeout,
          usingCache: false,
          usingProtocol: http.HttpProtocol.HTTP1_1,
          priority: 1,
          expectDataType: type,
          header: {
            'Accept-Encoding': 'identity',
            'Range': 'bytes=0-'
          }
        };

        // 构建 headers
        let headers = await this.buildHeaders();
        requestConfig.header = { ...requestConfig.header, ...headers };

        // 执行拦截器链 - 请求前
        let chain = new RequestChain(url, method, headers, data, null, requestConfig);
        for (let interceptor of this.interceptors) {
          chain = await interceptor.onRequest(chain);
        }

        // 发起请求获取文件大小
        const headResponse = await httpRequest.request(chain.url, {
          ...chain.requestConfig,
          method: http.RequestMethod.HEAD
        });

        const contentLength = parseInt(headResponse.header['content-length'] || '0');

        // 如果文件大于5MB，使用分段下载
        if (contentLength > 5 * 1024 * 1024) {
          return await this.downloadLargeFile(chain.url, contentLength, headers, options);
        }

        // 小文件直接下载
        const response = await httpRequest.request(chain.url, chain.requestConfig);

        // 执行拦截器链 - 响应后
        let responseChain = new ResponseChain(url, method, response);
        for (let interceptor of this.interceptors) {
          responseChain = await interceptor.onResponse(responseChain);
        }

        return this.handleResponse(responseChain.response);

      } catch (error) {
        if (this.isDownloadCancelled) {
          throw new HttpError(
            ServiceError.ERROR_CODE_CANCELLED,
            'Download cancelled'
          );
        }

        if (this.shouldRetry(error) && retryCount < this.maxRetries) {
          retryCount++;
          console.info(`Retrying request (${retryCount}/${this.maxRetries})`);
          await this.delay(this.getRetryDelay(retryCount));
          continue;
        }
        console.error(`Api Error (attempt ${retryCount + 1}/${this.maxRetries + 1}): ${error}`);
        lastError = error;
        return null;
      } finally {
        if (httpRequest) {
          httpRequest.destroy();
        }
      }
    }

    throw lastError;
  }

  private async downloadLargeFile(url: string, totalSize: number, headers: Record<string, string>,
    options?: DownloadOptions): Promise<http.HttpResponse> {

    const chunkSize = options?.chunkSize || this.defaultChunkSize;
    const chunks: ArrayBuffer[] = [];
    let downloadedSize = 0;
    let failedChunks: number[] = [];  // 记录下载失败的分块

    console.info(`Starting large file download: ${url}, total size: ${totalSize}`);

    while (downloadedSize < totalSize && !this.isDownloadCancelled) {
      const start = downloadedSize;
      const end = Math.min(start + chunkSize - 1, totalSize - 1);
      const currentChunkIndex = Math.floor(start / chunkSize);

      // 如果这个分块之前下载失败过，增加重试计数
      const retryCount = failedChunks.filter(chunk => chunk === currentChunkIndex).length;
      if (retryCount >= this.maxRetries) {
        throw new HttpError(
          ServiceError.ERROR_CODE_MAX_RETRIES,
          `Chunk ${currentChunkIndex} failed after ${this.maxRetries} retries`
        );
      }

      // 创建新的请求
      const httpRequest = http.createHttp();
      try {
        const response = await httpRequest.request(url, {
          method: http.RequestMethod.GET,
          header: {
            ...headers,
            'Range': `bytes=${start}-${end}`
          },
          readTimeout: options?.timeout || this.timeout,
          connectTimeout: options?.timeout || this.timeout,
          expectDataType: http.HttpDataType.ARRAY_BUFFER
        });

        if (response.result instanceof ArrayBuffer) {
          chunks[currentChunkIndex] = response.result;
          downloadedSize += response.result.byteLength;

          // 更新下载进度
          if (this.progressCallback || options?.onProgress) {
            const progress: DownloadProgress = {
              total: totalSize,
              current: downloadedSize,
              percentage: (downloadedSize * 100) / totalSize
            };
            this.progressCallback?.(progress);
            options?.onProgress?.(progress);
          }
        } else {
          // 如果没有收到预期的数据，记录失败并重试
          failedChunks.push(currentChunkIndex);
          console.error(`Chunk ${currentChunkIndex} failed: No data received`);
          continue;
        }

      } catch (error) {
        // 记录失败的分块，以便重试
        failedChunks.push(currentChunkIndex);
        console.error(`Chunk ${currentChunkIndex} failed:`, error);
        continue;
      } finally {
        httpRequest.destroy();
      }
    }

    if (this.isDownloadCancelled) {
      throw new HttpError(
        ServiceError.ERROR_CODE_CANCELLED,
        'Download cancelled'
      );
    }

    // 合并所有分块
    const result = new Uint8Array(totalSize);
    let offset = 0;
    for (const chunk of chunks) {
      if (!chunk) {
        throw new HttpError(
          ServiceError.ERROR_CODE_INCOMPLETE,
          'Incomplete download: missing chunks'
        );
      }
      result.set(new Uint8Array(chunk), offset);
      offset += chunk.byteLength;
    }

    // 构造响应对象
    const response: http.HttpResponse = {
      responseCode: 200,
      result: result.buffer,
      header: {
        'content-length': totalSize.toString(),
        'content-type': headers['content-type'] || 'application/octet-stream'
      }
    } as unknown as http.HttpResponse;

    return response;
  }

  private shouldRetry(error: any): boolean {
    if (error instanceof HttpError) {
      return error.code === -200 || // 网络断开
        error.code === 408 || // 请求超时
        error.code === 500 || // 服务器错误
        error.code === 502 || // 网关错误
        error.code === 503 || // 服务不可用
        error.code === 504;  // 网关超时
    }
    return false;
  }

  private getRetryDelay(retryCount: number): number {
    return Math.min(1000 * Math.pow(2, retryCount - 1), 10000);
  }

  private delay(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  setMaxRetries(retries: number): HttpDownloader {
    this.maxRetries = retries;
    return this;
  }

  setAgent(agent: string): HttpDownloader {
    this.agent = agent;
    return this;
  }

  setTimeout(milliseconds: number): HttpDownloader {
    this.timeout = milliseconds;
    return this;
  }

  setChunkSize(size: number): HttpDownloader {
    this.defaultChunkSize = size;
    return this;
  }

  addHeader(key: string, value: string): HttpDownloader {
    this.extraHeaders.set(key, value);
    return this;
  }

  removeHeader(key: string): HttpDownloader {
    this.extraHeaders.delete(key);
    return this;
  }

  addInterceptor(interceptor: HttpInterceptor): HttpDownloader {
    this.interceptors.push(interceptor);
    return this;
  }

  async get(url: string, options?: DownloadOptions): Promise<http.HttpResponse> {
    return this.request(http.RequestMethod.GET, url, null, http.HttpDataType.ARRAY_BUFFER, options);
  }

  async post(url: string, data?: any, options?: DownloadOptions): Promise<http.HttpResponse> {
    return this.request(http.RequestMethod.POST, url, data, http.HttpDataType.ARRAY_BUFFER, options);
  }

  async put(url: string, data?: any, options?: DownloadOptions): Promise<http.HttpResponse> {
    return this.request(http.RequestMethod.PUT, url, data, http.HttpDataType.ARRAY_BUFFER, options);
  }

  async delete(url: string, options?: DownloadOptions): Promise<http.HttpResponse> {
    return this.request(http.RequestMethod.DELETE, url, null, http.HttpDataType.ARRAY_BUFFER, options);
  }

  private async buildHeaders(additionalHeaders?: Map<string, string>): Promise<Record<string, string>> {
    let headerObj: Record<string, string> = await this.buildDefaultHeaders();

    this.extraHeaders.forEach((value, key) => {
      headerObj[key] = value;
    });

    if (additionalHeaders) {
      additionalHeaders.forEach((value, key) => {
        headerObj[key] = value;
      });
    }

    return headerObj;
  }

  private async buildDefaultHeaders(): Promise<Record<string, string>> {
    return {
      'Connection': 'keep-alive',
      'User-Agent': this.agent
    };
  }

  private handleResponse(response: http.HttpResponse): http.HttpResponse {
    if (response.responseCode >= 200 && response.responseCode < 300) {
      return response;
    } else if (response.responseCode >= 500 && response.responseCode < 600) {
      throw new HttpError(
        ServiceError.ERROR_CODE_500,
        'Server error'
      )
    } else if (response.responseCode >= 400 && response.responseCode < 500) {
      throw new HttpError(
        ServiceError.ERROR_CODE_400,
        'no uri'
      )
    } else {
      throw new HttpError(
        response.responseCode,
        'extra error'
      )
    }
  }
}